﻿// Copyright Lingsu
//
///-----------------------------------------------------------------
/// Shader:             RayCast
/// Description:        Volumetric ray-casting for VolumeComponents.
/// Author:             Zhouhua
///                     
///                     
/// Date:               April 30, 2017
/// Notes:              -
/// Revision History:   First release
/// 
///-----------------------------------------------------------------

Shader "Hidden/VolumeViewer/SimpleRayCast"
{
	Properties
	{
		
	}
	SubShader
	{
		Tags { "LightMode" = "ForwardBase" "VolumeTag" = "Volume" }
		Pass
		{
			Cull Front

			CGPROGRAM
				#pragma target 3.0
				#pragma vertex vert
				#pragma fragment frag

				#pragma exclude_renderers d3d9

				#define EPSILON_FLOAT	0.0001
				#define EPSILON_HALF	0.001
				#define SKIP_INTENSITY	0.01

				sampler3D data3D;
				sampler2D rayOffset2D;
				uniform half4 dataChannelWeight;
				uniform float nearClipPlane;
				uniform half leap;
				uniform half maxSamples;
				uniform half maxSlices;

				struct u2v {
					float4 	pos : POSITION;
					half2 	uv 	: TEXCOORD0;
				};

				struct v2f {
					float4 	pos 	: SV_POSITION;
					float3	osPos 	: TEXCOORD0;
					half2 	uv		: TEXCOORD1;
				};

				fixed cubeIntersection(float3 origin, float3 direction, float3 aabbMax, float3 aabbMin, out float tNear, out float tFar)
				{
					float3 invDir = 1.0 / direction;
					float3 t1 = invDir * (aabbMax - origin);
					float3 t2 = invDir * (aabbMin - origin);
					float3 tMin = min(t1, t2);
					float3 tMax = max(t1, t2);
					tNear = max(max(tMin.x, tMin.y), tMin.z);
					tFar = min(min(tMax.x, tMax.y), tMax.z);
					return tNear <= tFar;
				}

				half4 getColor(float3 pos)
				{
					half4 data = tex3Dlod(data3D, float4(pos.xyz, 0)) * dataChannelWeight;
					//data.a *= 0.5f;
					return data;
				}

				v2f vert(u2v v)
				{
					v2f o;
					o.pos = UnityObjectToClipPos(v.pos);
					o.osPos = v.pos.xyz;
					o.uv = v.uv;
					return o;
				}

				half4 frag(v2f i) : COLOR 
				{
					float3 rayOrigin = _WorldSpaceCameraPos.xyz;
					rayOrigin = mul(unity_WorldToObject, float4(rayOrigin, 1)).xyz;
					float3 rayDirection = normalize(i.osPos - rayOrigin);
					rayOrigin += rayDirection * nearClipPlane;
	    			float tNear, tFar;
					cubeIntersection(rayOrigin, rayDirection, float3(0.5, 0.5, 0.5), float3(-0.5, -0.5, -0.5), tNear, tFar);
					tNear *= tNear >= 0;
	    			float3 startRay = rayOrigin + rayDirection * tNear;
	    			float3 endRay = rayOrigin + rayDirection * tFar;

					float2 uv = i.pos.xy / _ScreenParams.xy;

	    			startRay =  startRay + 0.5;
	    			endRay =  endRay + 0.5;
					float maxRayLength = length(endRay - startRay);
					float rayLengthStep = 1.732 / maxSamples;
					float rayOffset = (tex2D(rayOffset2D, i.uv).r - 0.5) * rayLengthStep;
					float rayLength = rayOffset;

					half4 dst = 0;
					float opacityCorrection = maxSlices / maxSamples;

					half speed = max(1, ceil(maxSamples * leap / maxSlices));
					half skipProtection = 0;
					half samples = 0;
					[loop]
					while(rayLength < maxRayLength)
					{
						half4 src = getColor(startRay + rayLength * rayDirection);

						if (speed > 1.01)
						{
							skipProtection = speed - 1;
							rayLength -= rayLengthStep * skipProtection;
							speed = 1;
							continue;
						}
						else if (speed < 1.01 && skipProtection < 0.01)
						{
							speed = max(1, ceil(maxSamples * leap / maxSlices));
						}
						else if(skipProtection > 0.01)
						{
							skipProtection -= 1;
						}

						dst.r = src.r > dst.r ? src.r : dst.r;
						dst.a = src.a > dst.a ? src.a : dst.a;

						rayLength += rayLengthStep * speed;
						samples += 1;

						if (samples > maxSamples)
						{
							break;
						}
					}

					dst.g = dst.b = dst.r;

					return dst;
				}
			ENDCG
		}
	}
	FallBack Off
}